Lurch Deductive Engine

Tutorial: Serialization and attributes of LCs

Serialization and attributes of LCs

This tutorial assumes you know how to construct LC instances; if not, see the tutorial on Constructing LCs. This tutorial covers how to read and write attributes of LCs, plus how to serialize and deserialize them.

(Each piece of sample code below is written as if it were a script sitting in the root folder of this source code repository, and run from there with the command-line tools node. If you place your scripts in another folder, you will need to adjust the path in each import statement accordingly. If you have not yet set up a copy of this repository with the appropriate Node.js version installed, see our GitHub README, which explains how to do so.)

Reading and writing attributes

Every LC contains a mapping from strings to arbitrary JSON data. By default, this mapping is empty, but you can extend it as much as you like.



import { LogicConcept } from './src/index.js'

const L1 = LogicConcept.fromPutdown( '(just some expression)' )[0]
const L2 = LogicConcept.fromPutdown( '{ just some environment }' )[0]
L1.setAttribute( 'color', 'green' )
L2.setAttribute( 'height in cm', 100 )
console.log( L1.getAttribute( 'color' ) )
console.log( L2.getAttribute( 'color' ) )
console.log( L2.getAttributeKeys() )
// Console output:
green
undefined
[ 'height in cm' ]


Although large amounts of attribute data are awkward to write in putdown notation, it is possible to do so. The toPutdown() function supports the notation, as you can see below. Refer to the documentation for fromPutdown() for a full specification of the attribute notation.



console.log( L1.toPutdown() )
console.log( L2.toPutdown() )
// Console output:
(just some expression) +{"color":"green"}

{ just some environment } +{"height in cm":100}


It is often useful to be able to construct an LC and assign attributes to it in a single line of code. Thus the attr() function lets us add a whole dictionary of attributes at once, and it returns the original LC, for use in code like the following.



import { LurchSymbol, Environment } from './src/index.js'

const mushroom = new LurchSymbol( 'mushroom' )
const cluster = new Environment(
    mushroom.copy().attr( { 'size': 3, 'color': 'blue' } ),
    mushroom.copy().attr( { 'size': 1, 'color': 'pink' } ),
    mushroom.copy().attr( { 'size': 5, 'color': 'gray' } )
)
console.log( cluster.toPutdown() )
// Console output:
{
  mushroom
      +{"size":3}
      +{"color":"blue"}
  mushroom
      +{"size":1}
      +{"color":"pink"}
  mushroom
      +{"size":5}
      +{"color":"gray"}
}


Boolean attributes

It is also common to add a boolean property to an LC by setting an attribute with that name to "true," as in myLC.setAttribute( 'constant', 'true' ). Becauase this is common, we have convenience functions for adding, removing, and querying boolean attributes.



const ironMan = new LurchSymbol( 'Tony Stark' )
console.log( 'Before:', ironMan.isA( 'super hero' ) )
ironMan.makeIntoA( 'super hero' )
console.log( 'After:', ironMan.isA( 'super hero' ) )
ironMan.unmakeIntoA( 'super hero' )
console.log( 'Even later:', ironMan.isA( 'super hero' ) )
// Console output:
Before: false
After: true
Even later: false


There's also myLC.asA( 'foo' ) that makes a copy and adds the attribute to the copy, returning the copy.

Serialization and deserialization

We will often want to be able to save LCs somewhere, such as onto a user's filesystem, or into a browser's storage, or an online database. Thus we need a way to convert an LC tree into static data for saving, and then be able to later recreate the tree in memory from that static data. The static data format we choose is (naturally, since we're working in JavaScript) JSON.



import { MathConcept } from './src/index.js'

const saved = ironMan.toJSON()
console.log( saved )
const ironManCopy = MathConcept.fromJSON( saved )
console.log( ironManCopy.toPutdown() )
// Console output:
{
  className: 'Symbol',
  attributes: [ [ 'symbol text', 'Tony Stark' ] ],
  children: []
}
"Tony Stark"


Of course, we would use serialization for the saving and loading purposes mentioned above, which allow data to persist even when the app is not running. To just make a copy of an LC, we do not need to convert to and from JSON data; there is a copy() method for that purpose. Because two different LC trees can have the same structure, there is also a structural equality comparison method, equals().



const ironManCopy2 = ironMan.copy()
console.log( 'Same exact object?', ironMan == ironManCopy )
console.log( 'Same structure?', ironMan.equals( ironManCopy ) )
console.log( 'Same exact object?', ironMan == ironManCopy2 )
console.log( 'Same structure?', ironMan.equals( ironManCopy2 ) )
// Console output:
Same exact object? false
Same structure? true
Same exact object? false
Same structure? true